package com.hp.bon.sgw.wh.channel;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import org.simpleframework.http.core.Container;
import org.simpleframework.http.core.ContainerProcessor;
import org.simpleframework.http.core.ContainerSelector;
import org.simpleframework.http.core.ContainerServer;
import org.simpleframework.transport.connect.Connection;
import org.simpleframework.transport.connect.SocketConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.bon.sgw.Constants;
import com.hp.bon.sgw.Constants.NodeStatus;
import com.hp.bon.sgw.Constants.ResultCode;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.core.BuzProcessorManager;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.core.InprogressMessageQueue;
import com.hp.bon.sgw.core.channel.IChannel;
import com.hp.bon.sgw.domain.Node;
import com.hp.bon.sgw.domain.SGWChannel;
import com.hp.bon.sgw.service.ConfigListener;
import com.hp.bon.sgw.service.ConfigService;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.wh.Constants.AliParam;
import com.hp.bon.sgw.wh.channel.handler.SignTaobaoHandler;
import com.hp.bon.sgw.wh.service.WHConfigService;
import com.hp.bon.sgw.wh.utils.DESCryptoUtil;
import com.hp.bon.sgw.wh.utils.SignUtilsImpl;
import com.hp.ocs.diameter.DiameterUtils;
import com.hp.ocs.diameter.message.AVP;
import com.hp.ocs.diameter.message.AVP_Grouped;
import com.hp.ocs.diameter.message.AVP_UTF8String;
import com.hp.ocs.diameter.message.InvalidAVPLengthException;
import com.hp.ocs.diameter.message.Message;

/**
 * 
 * @ClassName: TaobaoWarringChannel   
 * @Description: 已经合并到TaobaoChannel.java中  
 * @author: Sue  
 * @date:2014年11月20日 下午5:46:50
 */
public class TaobaoWarringChannel implements Container ,ConfigListener ,IChannel {
	
	/**
	 * Logger for this class
	 * */
	private static final Logger logger = LoggerFactory.getLogger(TaobaoWarringChannel.class) ;
	
	private ContainerServer server = null ;
	private SGWChannel channel ;
	protected ConfigService confSrv ;
	private MultiThreadedHttpConnectionManager connectionManager ;
	private HttpClient httpClient;
	private WHConfigService whConfigService;
	
	public TaobaoWarringChannel(SGWChannel channel){
		this.channel = channel ;
		confSrv = SpringContext.getInstance().getConfigService() ;
		whConfigService = SpringContext.getApplicationContext().getBean(WHConfigService.class) ;
	}
	
	ThreadPoolExecutor executor;
	ThreadPoolExecutor collector;
	
	@Override
	public void start() throws IOException {
		logger.info("start TaobaoWarringChannel ...");
		String key = "sgw.taobaochannel.thread.max";
		int maxThreads = confSrv.getIntValue(key, 20);
		logger.info(key + " :" + maxThreads);
		String endPoint = this.channel.getUri();
		if (endPoint == null) {
			logger.warn("channel dont't defined uri");
			return;
		}
		logger.info("start sgw TaobaoWarringChannel server portal at " + endPoint);
		try {
			URL srvURL = new URL(endPoint);
			int port = srvURL.getPort();
			if (port == -1) {
				logger.info("start taobaoWarringChannel error : port Not defined");
				return ;
			}
			String host = srvURL.getHost();
			if (host.equals("127.0.0.1") || host.equalsIgnoreCase("localhost")) {
				host = "0.0.0.0";
			}
			
			server = new ContainerServer(this, maxThreads);
			Connection connection = new SocketConnection(server);
			SocketAddress address = new InetSocketAddress(host, port);
			connection.connect(address);
			
			collector = ((ContainerSelector) ((ContainerProcessor) server.getProcessor()).getSelector()).getCollect().getQueue();
			executor = ((ContainerSelector) ((ContainerProcessor) server.getProcessor()).getSelector()).getExecutor().getQueue();
			
			connectionManager = new MultiThreadedHttpConnectionManager();
			httpClient = new HttpClient(connectionManager);
			httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(10000);
			httpClient.getHttpConnectionManager().getParams().setSoTimeout(45000);

			this.channel.setStatus(NodeStatus.ON_LINE);
			confSrv.updateChannelStatus2DB(this.channel.getId(), NodeStatus.ON_LINE);
		} catch (IOException e) {
			logger.error("start TaobaoWarringChannel " + this.channel.toString()+ " err " + e);
		}
	}
	
	@Override
	public void stop() {
		logger.info("stop TaobaoWarring Channel") ;
		if (server != null) {
			try {
				server.stop();
				logger.info("stop server: ");
			} catch (IOException e) {
				logger.warn("stop server IOException:",e);
			}
		}
		if (connectionManager != null) {
			connectionManager.shutdown();
		}
		
		this.channel.setStatus(NodeStatus.OFF_LINE) ;
		confSrv.updateChannelStatus2DB(this.channel.getId(), NodeStatus.OFF_LINE);
		
		try {
			logger.info("wait 3s to exit...");
			Thread.sleep(3*1000);
		} catch (InterruptedException e) {
			logger.warn("Thread.sleep InterruptedException:",e);
		}
	}
	
	/**
	 * 实现告警接口 (收到HB的DCC请求，转换成HTTP请求发送给淘宝)
	 * */
	@Override
	public int sendRequest(CallParam callParam) {
		
		if (logger.isDebugEnabled()) {
			logger.debug(callParam.getSessionId() + ", prepare forward request to TaoBao:" + callParam.getToNode().getServerUrl());
		}
		
		String algorithm = whConfigService.getValue(AliParam.algorithm, "SHA1withRSA");
		String priKeyValue = whConfigService.getValue(AliParam.priKeyValue, "");
		String pubKeyValue = whConfigService.getValue(AliParam.pubKeyValue, "");
		String aliPubKeyValue = whConfigService.getValue(AliParam.aliPubKeyValue, "");
		
		Message request = (Message) callParam.getRequest();
		AVP avp = null;
		String signTag = null;
		String sendMSG = null ;
		String signMSG = null ;
		String encryptMSG = null ;
		String responseMSG = null ;
		
		StringBuilder builder = new StringBuilder();
		StringBuilder signData = new StringBuilder() ;
		
		try {
			avp = request.find(Constants.DIAMETER_AVP_SERVICE_INFOMATION,Constants.DIAMETER_VENDOR_3GPP);
			if (avp != null) {
				AVP_Grouped serviceInfsAVP = new AVP_Grouped(avp);
				// 机构缩写(支付宝分配)
				AVP instIdAVP = serviceInfsAVP.find(92106,Constants.DIAMETER_VENDOR_CT);
				String instId = new AVP_UTF8String(instIdAVP).queryValue();
				// 手机号
				AVP accNbrAVP = serviceInfsAVP.find(80125,Constants.DIAMETER_VENDOR_CT);
				String accNbr = AVP_UTF8String.queryValue(accNbrAVP);
				// 运营商内部唯一标识
				AVP operatorUserIdAVP = serviceInfsAVP.find(90010,Constants.DIAMETER_VENDOR_CT);
				String operatorUserId = AVP_UTF8String.queryValue(operatorUserIdAVP);
				// 预警值
				AVP alertValueAVP = serviceInfsAVP.find(92107,Constants.DIAMETER_VENDOR_CT);
				String alertValue = new AVP_UTF8String(alertValueAVP).queryValue();
				// 当前数值：分为单位
				AVP currentValueAVP = serviceInfsAVP.find(92108,Constants.DIAMETER_VENDOR_CT);
				String currentValue = AVP_UTF8String.queryValue(currentValueAVP);
				// 业务类型
				AVP businessTypeAVP = serviceInfsAVP.find(92109,Constants.DIAMETER_VENDOR_CT);
				String businessType = AVP_UTF8String.queryValue(businessTypeAVP);
				//出发预警时间
				AVP occurTimeAVP = serviceInfsAVP.find(92110,Constants.DIAMETER_VENDOR_CT);
				String occurTime = AVP_UTF8String.queryValue(occurTimeAVP);
				//运营商唯一流水
				AVP alertNoAVP = serviceInfsAVP.find(92101,Constants.DIAMETER_VENDOR_CT);
				String alertNo = AVP_UTF8String.queryValue(alertNoAVP);

				builder.delete(0, builder.length());
				
				SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd hh:mm:ss") ;
				Date date = new Date(System.currentTimeMillis());
				
				//部分字段要联系taobao确认
				builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>")
						.append("<message>")
						.append("<head>")
						.append("<version>").append("1.0.1").append("</version>")
						.append("<instId>").append(instId).append("</instId>")
						.append("<type>AlertNotifyReq</type>")
						.append("<msgId>").append(callParam.getSessionId()).append("</msgId>")
						.append("<msgTime>").append(sdf.format(date)).append("</msgTime>")
						.append("<secId>").append("0002").append("</secId>")
						.append("</head>")
						.append("<body>")
						.append("<instId>").append(instId).append("</instId>")
						.append("<userNumber>").append(accNbr).append("</userNumber>")
						.append("<operatorUserId>").append(operatorUserId).append("</operatorUserId>")
						.append("<alertValue>").append(alertValue).append("</alertValue>")
						.append("<currentValue>").append(currentValue).append("</currentValue>")
						.append("<businessType>").append(businessType).append("</businessType>")
						.append("<occurTime>").append(occurTime).append("</occurTime>")
						.append("<alertNo>").append(alertNo).append("</alertNo>")
						.append("</body>") ;
						
				signData.append(instId).append(accNbr).append(operatorUserId).append(alertValue)
				        .append(currentValue).append(businessType).append(occurTime).append(alertNo);
				
				signMSG = signData.toString() ;
				signTag = SignUtilsImpl.sign(signMSG, priKeyValue, algorithm, "UTF-8");
				
				builder.append("<signTag>").append(signTag).append("</signTag>")
					   .append("</message>") ;
						
				
				sendMSG = builder.toString();
				logger.info("SendMsg to taobao ： "+sendMSG);
				//发送前对整个报文-bulider进行加密（DES加密）
				encryptMSG = DESCryptoUtil.encrypt(sendMSG,pubKeyValue,null) ;
				
				PostMethod method = new PostMethod(callParam.getToNode().getServerUrl());
				
				String response = null;
				try {
					// 记录转发request信息.
					XmlMessage sendRequest = new XmlMessage(true);
					sendRequest.setMsgTime(System.currentTimeMillis());
					sendRequest.setMID(callParam.getSessionId());
					sendRequest.setSCID(callParam.getServiceContextId());
					sendRequest.setLength(sendMSG.length());
					sendRequest.setDATA(sendMSG);
					callParam.setSendRequest(sendRequest);
					
					RequestEntity requestEntity = new StringRequestEntity(encryptMSG, "text/xml", "UTF-8");
					method.setRequestEntity(requestEntity);
					
					int statusCode = httpClient.executeMethod(method);

					if (statusCode != HttpStatus.SC_OK) {
						logger.warn(callParam.getSessionId() + ", statusCode:"+statusCode+" sendRequest:Method failed: " + method.getStatusLine());
						callParam.setRetCode(ResultCode.NE_CANNOT_USE);
						callParam.setResultMsg("Call taobao service err：Method failed. " + method.getStatusLine());
					}
					
					BufferedReader reader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream()));
					StringBuffer stringBuffer = new StringBuffer();
					String str = "";
					while((str = reader.readLine())!=null){
						stringBuffer.append(str);
					}
					response = stringBuffer.toString();
					
					logger.info("Receive Encryption Message From taobao : "+response);
					
					//对报文 解密
					responseMSG = DESCryptoUtil.decrypt(response, pubKeyValue,null) ;
					logger.info(callParam.getSessionId()+ ", get decrypted respones from taobao:" + responseMSG);

				} catch (Exception e) {
					e.printStackTrace();
					callParam.setRetCode(ResultCode.NE_CANNOT_USE);
					callParam.setResultMsg("Call taobao service err："
							+ e.getLocalizedMessage());
				} finally {
					if (method != null) {
						method.releaseConnection();
					}
				}

				if (responseMSG != null) {
					Document doc = null;
					String rspStatus = null;
					String rspFailedCode = null;
					String rspFailedReason = null;
					String respSignTag = null ;
					try {
						doc = DocumentHelper.parseText(responseMSG);
						Element root = doc.getRootElement();

						org.dom4j.Node node = root.selectSingleNode("//message/body/status");
						if (node != null) {
							// 成功标志
							rspStatus = node.getStringValue();
						}
						node = root.selectSingleNode("//message/body/failedCode");
						if (node != null) {
							rspFailedCode = node.getStringValue();
						}
						node = root.selectSingleNode("//message/body/failedReason");
						if (node != null) {
							rspFailedReason = node.getStringValue();
						}
						node = root.selectSingleNode("//message/signTag") ;
						if(node != null){
							respSignTag = node.getStringValue() ;
						}
						
					} catch (DocumentException e1) {
						logger.warn("Document Exception : "+e1);
					}
					
					String respBody=rspStatus+rspFailedCode+rspFailedReason+"";
					
					//验签 （验签时是否要用toabao提供的签名公钥）
					if( !SignUtilsImpl.verify(respBody, respSignTag, aliPubKeyValue, algorithm, "UTF-8")) {
						logger.warn("Signature verification failed...");
						return -1 ;
					}
					
					// 封装淘宝的response
					Element serviceInformation = DocumentHelper.createElement("Service_Information");
					Element element = null;
					if (rspStatus != null) {
						if(rspStatus.equalsIgnoreCase("SUCCESS")){
							element = serviceInformation.addElement("Status");
							element.setText(rspStatus);
						}else{
							element = serviceInformation.addElement("Status");
							element.setText(rspStatus);
							if(rspFailedCode != null){
								element = serviceInformation.addElement("failedCode");
								element.setText(rspFailedCode);
							}
							if(rspFailedReason != null){
								element = serviceInformation.addElement("failedReason");
								element.setText(rspFailedReason);
							}
						}
					}else{
						logger.info("response error ...");
					}

					logger.info(callParam.getSessionId() + ", TaoBaoBalWarring response to XML:" + serviceInformation.asXML());

					XmlMessage ack = new XmlMessage(false);
					ack.setMsgTime(System.currentTimeMillis());
					ack.setMID(callParam.getSessionId());
					ack.setSIG(null);
					ack.setRC("0");
					ack.setDS("");
					ack.setRTT("");
					ack.setREPTAG(0);
					ack.setLength(serviceInformation.asXML().length());
					ack.setDATA(serviceInformation.asXML());
					ack.setDataElement(serviceInformation);

					callParam.setRecvResponse(ack);
				}
			}
		} catch (InvalidAVPLengthException e) {
			e.printStackTrace();
			logger.warn(callParam.getSessionId() + ",convert DCC to XML error: InvalidAVPLengthException");
			callParam.setRetCode(ResultCode.VALUE_ERROR);
			callParam.setResultMsg("convert DCC to XML error:" + e.getLocalizedMessage());
		}

		CallParam _callParam = InprogressMessageQueue.getInstance().removeProgress(callParam.getMessageUniqueID());
		if (logger.isDebugEnabled()) {
			logger.debug("remove progress form queue:" + callParam.getMessageUniqueID());
		}
		if (_callParam == null) {
			logger.warn("msg queue not find callParam for " + callParam.getMessageUniqueID());
		}

		callParam.getAsynHandler().onCallResult(callParam);
		return 0;
	
	}
	
	private static String path = "tbsign";//TODO确定发过来的地址

	@Override
	public void handle(Request request, Response response) {
		
		logger.info("receive request from " + request.getClientAddress() + "; "	+ request.toString());
		
		
		String algorithm = whConfigService.getValue(AliParam.algorithm, "SHA1withRSA");
//		String priKeyValue = whConfigService.getValue(AliParam.priKeyValue, "");
		String pubKeyValue = whConfigService.getValue(AliParam.pubKeyValue, "");
		String aliPubKeyValue = whConfigService.getValue(AliParam.aliPubKeyValue, "");
		
		long receiveTime = System.currentTimeMillis();
		PrintStream body = null;
		
		try {
			body = response.getPrintStream();
			response.set("Content-Type", "text/xml;charset=UTF-8");
			response.set("Server", "HP Asyn Server )");
			response.setDate("Date", receiveTime);
			response.setDate("Last-Modified", receiveTime);

			if (!request.getPath().getPath().endsWith(path)) {
				logger.debug("request path is invalid:"	+ request.getPath().getPath()+ " , ignore this request.");
				return;
			}

			// TODO 增加IP校验
			String IP = request.getClientAddress().getAddress().getHostAddress();
			
		
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(request.getInputStream(),"utf-8")); 
	        StringBuffer stringBuffer =new StringBuffer("");
	        String temp;
	        while((temp=bufferedReader.readLine())!=null){
	            stringBuffer.append(temp);
	        }
	        bufferedReader.close();
	        String xml = stringBuffer.toString();
	        
	        String respXml = "" ;
	        respXml = DESCryptoUtil.decrypt(xml, pubKeyValue,null) ;
	        logger.info("response from taobao:"+respXml);
	        
	        String rspUserNumber = "";
			String rspAlertValue = "";
			String rspSignType = "";
			String rspBusinessType = "" ;
			String rspSignTime = "";
			String rspSignTag = "";
//			String rspInstId = "";
			String rspMsgId = "";
	        
	        if (respXml != null) {
				Document doc = null;
				doc = DocumentHelper.parseText(respXml);
				Element root = doc.getRootElement();
				
				org.dom4j.Node node = root.selectSingleNode("//message/body/userNumber");
				if (node != null) {
					rspUserNumber = node.getStringValue();
				}
				node = root.selectSingleNode("//message/body/alertValue");
				if (node != null) {
					rspAlertValue = node.getStringValue();
				}
				node = root.selectSingleNode("//message/body/signType");
				if (node != null) {
					rspSignType = node.getStringValue();
				}
				node = root.selectSingleNode("//message/body/businessType") ;
				if(node != null){
					rspBusinessType = node.getStringValue() ;
				}
				node = root.selectSingleNode("//message/body/signTime") ;
				if(node != null){
					rspSignTime = node.getStringValue() ;
				}
				node = root.selectSingleNode("//message/signTag") ;
				if(node != null){
					rspSignTag = node.getStringValue() ;
				}
				node = root.selectSingleNode("//message/head/msgId") ;
				if(node != null){
					rspMsgId = node.getStringValue() ;
				}
	        }
	        String respBody=rspUserNumber+rspAlertValue+rspSignType+rspBusinessType+rspSignTime+"";
	        
			//验签 需要淘宝提供公钥和解密的秘钥
			if( !SignUtilsImpl.verify(respBody, rspSignTag, aliPubKeyValue, algorithm, "UTF-8")) {
				logger.info("Signature verification failed ...");
				return ;
			}
				
			// 封装淘宝的request
			Element serviceInformation = DocumentHelper.createElement("Service_Information");
			Element element = null; 
			if (rspUserNumber != null && !"".equals(rspUserNumber)) {
				element = serviceInformation.addElement("Acc_Nbr");
				element.setText(rspUserNumber);
			}
			if (rspAlertValue != null && !"".equals(rspAlertValue)) {
				element = serviceInformation.addElement("alertValue");
				element.setText(rspAlertValue);
			}
			if (rspSignType != null && !"".equals(rspSignType)) {
				element = serviceInformation.addElement("signType");
				element.setText(rspSignType);
			}
			if (rspBusinessType != null && !"".equals(rspBusinessType)) {
				element = serviceInformation.addElement("businessType");
				element.setText(rspBusinessType);
			}
			if (rspSignTime != null && !"".equals(rspSignTime)) {
				element = serviceInformation.addElement("occurTime");
				element.setText(rspSignTime);
			}
			
			String ALI_HOST = whConfigService.getValue(AliParam.aliSignHost,"Alibaba1@027.ctnbc-bon.net");
			// 将请求消息转换成DCC消息对应格式的XmlMessage
			XmlMessage req = new XmlMessage(true);
			String sessionId = DiameterUtils.makeSessionID(ALI_HOST);

			req.setIp(IP);
			req.setMsgTime(receiveTime);
			req.setMID(sessionId);
			req.setUID(ALI_HOST);
			req.setSCID(whConfigService.getValue(AliParam.aliSignSCID, "BalanceWarningSign.Taobao@027.ctnbc-bon.net"));
			req.setSIG(null);
			req.setREPTAG(0);
			req.setDATA(serviceInformation.asXML());
			req.setDataElement(serviceInformation);

			// new CallParam对象
			SignTaobaoHandler handler = new SignTaobaoHandler(body, rspMsgId, whConfigService);
			CallParam callParam = new CallParam(req.getMID(), req.getSCID(),req, req.getUID(), handler);
			BuzProcessorManager.getInstance().processRequest(callParam);

		} catch (Exception e) {
			logger.warn("Exception:"+e);
		}
	}
	
	@Override
	public void addPersist(Node arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void deletePersist(Node arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public int getNodeSendQueueSize(String arg0) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public SGWChannel getSGWChannel() {
		return this.channel ;
	}

	@Override
	public int getThreadPoolActiveCount() {
		return executor.getActiveCount();
	}

	@Override
	public int getThreadPoolQueueSize() {
		return executor.getQueue().size();
	}

	@Override
	public int getThreadPoolSize() {
		return executor.getPoolSize();
	}

	@Override
	public Set<String> monitorConfKeys() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void onConfigUpdated(String arg0) {
		// TODO Auto-generated method stub
		
	}


}
